#include <yunpan.h>
typedef struct train_s{
    int dataLength;
    char buf[1000];
}train_t;

typedef struct task_s{
    int netFd;
    struct task_s *pNext;
    char buf[1024];//存客户端发过来的命令字符串
}task_t;
int epollAdd(int fd, int epfd){
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = fd;
    int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event);
    ERROR_CHECK(ret,-1,"epoll_ctl");
    return 0;
}
int epollDel(int fd, int epfd){
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = fd;
    int ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&event);
    ERROR_CHECK(ret,-1,"epoll_ctl");
    return 0;
}
int recvn(int netFd,void *p, int n){
    int total = 0;
    int ret;
    char *pTmp = (char *)p;
    while(total < n){
        ret = recv(netFd,pTmp+total,n-total,0);//pTmp+total是偏移量
        ERROR_CHECK(ret,-1,"recv");
        total+= ret;
    }
    return 0;
}
int recvFile(task_t *pTask){
    struct stat fileBuf;//用来计算本地文件大小    
    int result;
    int len = strlen(pTask->buf);
    pTask->buf[len-1]=0;//去掉回车，获得真正的file名

    int fd = open(pTask->buf, O_RDWR|O_CREAT, 0666);//没有就新建，有就打开
    ERROR_CHECK(fd,-1,"open");

    result = stat(pTask->buf, &fileBuf);
    long localFileLen = fileBuf.st_size;//本地文件大小
    //printf("result = %d,filebufsize = %ld\n",result,fileBuf.st_size);
    char beforeFilesize[20]={0};
    sprintf(beforeFilesize,"%ld",fileBuf.st_size);//把本地文件大小(long型)转化成字符串发给服务端
    printf("beforeFilesize = %s,size = %lu\n",beforeFilesize,sizeof(beforeFilesize)) ;
    send(pTask->netFd,beforeFilesize,strlen(beforeFilesize),0);

    int dataLength;
    // int ret =recvn(pTask->netFd,&dataLength,sizeof(int));//先收火车头
    // ERROR_CHECK(ret,-1,"recv1");
    // ret = recvn(pTask->netFd,name, dataLength);//再收文件名
    // ERROR_CHECK(ret,-1,"recv2");

    long fileSize;
    recvn(pTask->netFd,&dataLength,sizeof(int));//先收火车头
    recvn(pTask->netFd,&fileSize,sizeof(fileSize));//再收文件长度
    printf("fileSize = %ld\n", fileSize);

    //对比进行判断
    if (localFileLen == 0) {
        printf("No local file !\n");
        lseek(fd, localFileLen, SEEK_SET);
    }
    else if (localFileLen < fileSize) {
        lseek(fd, localFileLen, SEEK_SET);
        printf("Local file has existed! Download is continuing!\n");
    }
    else {
        printf("Have recv success!\n");
        return 0;
    }

    //ftruncate(fd,fileSize);
    //接收文件
    char buf[1024] = {0};
    off_t downSize = localFileLen;
    off_t lastSize = localFileLen;
    off_t slice = fileSize/1000;
    struct timeval begTime,endTime;
    gettimeofday(&begTime,NULL);
    while(1){
        int ret = recvn(pTask->netFd,&dataLength,sizeof(int));//先收火车头
        ERROR_CHECK(ret,-1,"recv");
        if(dataLength==0){
            break;
        }
        if(dataLength != 1000){
            printf("dataLength = %d\n", dataLength);
        }
        ret = recvn(pTask->netFd,buf,dataLength);
        ERROR_CHECK(ret,-1,"recv");
        downSize += dataLength;
        if(downSize-lastSize > slice){
            printf("%5.2lf%s\r", 100.0*downSize/fileSize,"%");
            fflush(stdout);
            lastSize = downSize;
        }
        write(fd,buf,dataLength);
    }
    printf("100.00%s\n", "%");
    gettimeofday(&endTime,NULL);
    printf("total time = %ld us\n", 1000000*(endTime.tv_sec-begTime.tv_sec) + endTime.tv_usec-begTime.tv_usec);

}

int sendFile(task_t *pTask){
    train_t t;
    char *command = strtok(pTask->buf," ");//解析获取命令
    char *fileName = strtok(NULL," ");//解析获取文件名
    int length = strlen(fileName);
    
    bzero(t.buf,sizeof(t.buf));
    t.dataLength = length;
    strcpy(t.buf,fileName);

    //printf("length = %d,fileName = %s\n",length,fileName);
    
    int fd = open(fileName, O_RDWR);
    ERROR_CHECK(fd,-1,"open");
    int ret = send(pTask->netFd,&t,sizeof(t.dataLength)+ t.dataLength,MSG_NOSIGNAL);//发送文件名
    ERROR_CHECK(ret,-1,"send");
    struct stat statbuf;
    ret = fstat(fd,&statbuf);//获取文件的总大小
    ERROR_CHECK(ret,-1,"fstat");
    t.dataLength = sizeof(statbuf.st_size);//把文件的大小装入小火车
    memcpy(t.buf,&statbuf.st_size,sizeof(statbuf.st_size));
    send(pTask->netFd,&t,sizeof(t.dataLength)+t.dataLength,MSG_NOSIGNAL);
    
    char *p = (char *)mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    ERROR_CHECK(p,MAP_FAILED,"mmap");
    off_t total = 0;
    while(total < statbuf.st_size){
        if(statbuf.st_size - total < sizeof(t.buf)){
            //最后一次发送
            t.dataLength = statbuf.st_size-total;
        }
        else{
            t.dataLength = sizeof(t.buf);
        }
        memcpy(t.buf,p+total,t.dataLength);//把数据装入火车车厢
        int ret = send(pTask->netFd,&t,sizeof(t.dataLength)+t.dataLength,MSG_NOSIGNAL);
        ERROR_CHECK(ret,-1,"send");
        total += t.dataLength;
    }
    munmap(p,statbuf.st_size);
    t.dataLength = 0;
    send(pTask->netFd,&t,sizeof(t.dataLength),0);
    //发送终止
    close(fd);
}
int main(int argc, char *argv[])
{
    //./clientItem2 172.22.123.229 1234
    //建立与服务器端连接
    ARGS_CHECK(argc, 3);
    int sockFd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    int ret = connect(sockFd, (struct sockaddr *)&addr, sizeof(addr));
    ERROR_CHECK(ret, -1, "connect");

    char enterOrAdd[3] = {0};
    printf("用户登录请按 1 \n创建用户并登录请按 2\n");
    read(STDIN_FILENO, enterOrAdd, 2);
    send(sockFd, enterOrAdd, 3, 0);
    if(strcmp(enterOrAdd,"2\n")==0)
    {
        char userName[20] = {0} ;
        char *password;
        char callback[20] = {0};
        printf("请输入用户名：\n");
        ret = read(STDIN_FILENO, userName, sizeof(userName));
        ret = send(sockFd, userName, strlen(userName) - 1, 0);
        password = getpass("请输入密码：");
        ret = send(sockFd, password, strlen(password), 0);
        recv(sockFd, callback, sizeof(callback), 0);
        if(atoi(callback) == 5)
            {
                puts("Create successfully");
            }
    }
    else if(strcmp(enterOrAdd,"1\n")==0)
    {
        while(1)
        {
            char userName[20] = {0} ;
            char *password;
            char callback[20] = {0};
            printf("请输入用户名：\n");
            ret = read(STDIN_FILENO, userName, sizeof(userName));
            ERROR_CHECK(ret ,-1, "read userName");
            ret = send(sockFd, userName, strlen(userName) - 1, 0);
            ERROR_CHECK(ret, -1, "send userName");
            password = getpass("请输入密码：");
            puts(password);
            ret = send(sockFd, password, strlen(password), 0);
            ERROR_CHECK(ret, -1, "send password");
            
            ret = recv(sockFd, callback, sizeof(callback), 0);
            if(atoi(callback) == 5)
            {
                puts("Connect successfully");
                break;
            }
            else if(atoi(callback) == -1)
            {
                puts("Connect failed, please try again");
            }
        }
    }
    

    //recvFile(sockFd);
    //输入指令并传输。
    char order[100] = {0};
    int epfd = epoll_create(10);
    epollAdd(sockFd, epfd);
    epollAdd(STDIN_FILENO, epfd);
    struct epoll_event readyList[2];
    char buf[100];
    //task_t *pTask;
    printf("Please input your order:\n");
    while (1)
    {
        int readyNum = epoll_wait(epfd, readyList, 2, -1);
        for (int i = 0; i < readyNum; ++i)
        {
            bzero(buf,sizeof(buf));
            if(readyList[i].data.fd == sockFd){
                int ret = recv(sockFd,buf,sizeof(buf),0);
                if(ret == 0){
                    //puts("I cant recv info!");
                    continue;
                }
                if(strcmp(buf,"-1")==0){
                    printf("Please input right order!\n");
                }
                puts(buf);
                //printf("Please input your order:\n");
                //recvFile(sockFd);
            }
            if(readyList[i].data.fd==STDIN_FILENO){
                int ret = read(STDIN_FILENO,buf,sizeof(buf));
                //puts("read");
                if(ret==0){
                    exit(0);
                }
                send(sockFd,buf,strlen(buf)-1,0);
                //puts("send");
                if(strncmp(buf,"gets ",5)==0){
                    task_t *pTask = (task_t*)calloc(1,sizeof(task_t));
                    pTask->netFd = sockFd;
                    char *cond = strtok(buf, " ");//命令
                    char *dirName = strtok(NULL, " ");//文件名
                    strcpy(pTask->buf,dirName);
                    puts("strcpy succ");
                    recvFile(pTask);
                }
                
                if(strncmp(buf,"puts ",5)==0){
                    //puts("puts");
                    // char *command = strtok(buf," ");//解析获取命令
                    // char *fileName = strtok(NULL," ");//解析获取文件名
                    // char newFileName[100] = {0};
                    // strncpy(newFileName,fileName,strlen(fileName)-1);
                    task_t *pTask = (task_t*)calloc(1,sizeof(task_t));
                    pTask->netFd = sockFd;
                    strncpy(pTask->buf,buf,strlen(buf)-1);
                    sendFile(pTask);
                }
            }
        }
    }
}